package com.binarycat.dynamiclimit.limiter;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;

import com.binarycat.dynamiclimit.DynamicLimiterManager;
import com.binarycat.dynamiclimit.annotation.DynamicLimit;
import com.binarycat.dynamiclimit.bean.DynamicLimitData;
import com.binarycat.dynamiclimit.bean.DynamicLimiterConfig;
import com.binarycat.dynamiclimit.bean.MethodMappingLimitData;
import com.binarycat.dynamiclimit.initialization.RateLimiterFactory;
import com.binarycat.dynamiclimit.limiter.policy.AbstractLimitPolicy;
import com.binarycat.dynamiclimit.limiter.policy.AbstractMethodLevelLimitPolicy;
import com.binarycat.dynamiclimit.limiter.policy.AbstractSystemLevelLimitPolicy;
import com.binarycat.dynamiclimit.util.CommonLimitUtils;
import com.binarycat.dynamiclimit.util.LimitRateLogUtils;
import com.google.common.util.concurrent.RateLimiter;

/**
 * 动态限流器
 * 
 * @author jjb
 * @date 2018/08/06
 */
public class DynamicLimiter {

    /**
     * 获取限流状态
     * 
     * @param handler
     * @param limitData
     * @param limiterConfig
     * @param handler2
     * 
     * @return true:已限流,false:未限流
     */
    public boolean acquireLimitStatus(HttpServletRequest request, Object handler, DynamicLimitData limitData,
        DynamicLimiterConfig limiterConfig) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod)handler;

            DynamicLimit dynamicLimit = CommonLimitUtils.getDynamicLimitAnnotation(method);
            if (dynamicLimit == null) {
                return false;
            }

            Map<String, MethodMappingLimitData> methodRateLimiterMap = limitData.getMethodMappingLimitDataMap();
            MethodMappingLimitData methodMappingLimitData;
            if (dynamicLimit.limitByIp()) {
                methodMappingLimitData = RateLimiterFactory.getMethodMappingLimitData(limitData, method, request);
            } else {
                methodMappingLimitData = methodRateLimiterMap.get(method.toString());
            }

            RateLimiter rateLimiter = methodMappingLimitData.getRateLimiter();
            if (methodMappingLimitData.getLimitRateStatus().get() == true) {
                // 已限流
                if (!rateLimiter.tryAcquire()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 执行动态限流策略
     * 
     * @param dynamicLimiterManager
     */
    public void excuteDynamicLimitPolicy(DynamicLimiterManager dynamicLimiterManager) {
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap =
            CommonLimitUtils.getHandlerMethodMap(dynamicLimiterManager);
        List<AbstractLimitPolicy> dynamicLimitPolicyList = dynamicLimiterManager.getDynamicLimitPolicyList();
        
        // 执行系统级别的限流策略--------------------
        for (AbstractLimitPolicy abstractDynamicLimitPolicy : dynamicLimitPolicyList) {

            if (!(abstractDynamicLimitPolicy instanceof AbstractSystemLevelLimitPolicy)) {
                continue;
            }
            abstractDynamicLimitPolicy.initLimitPolicyBaseData(dynamicLimiterManager, null);
            
            if (abstractDynamicLimitPolicy.supportLimitPolicy()) {
                abstractDynamicLimitPolicy.executeLimitPolicy();
            }
        }

        // 执行方法级别的限流策略----------------------
        for (Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
            HandlerMethod handlerMethod = entry.getValue();
            for (AbstractLimitPolicy abstractDynamicLimitPolicy : dynamicLimitPolicyList) {

                if (!(abstractDynamicLimitPolicy instanceof AbstractMethodLevelLimitPolicy)) {
                    continue;
                }

                abstractDynamicLimitPolicy.initLimitPolicyBaseData(dynamicLimiterManager, handlerMethod);
                if (abstractDynamicLimitPolicy.supportLimitPolicy()) {
                    abstractDynamicLimitPolicy.executeLimitPolicy();

                }
            }
        }

    }

}
